package fr.generali.accueilclient.gwt.client.ui.components.table.cells;

import com.google.common.base.Objects;
import com.google.gwt.cell.client.AbstractEditableCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.user.client.Timer;

import fr.generali.accueilclient.gwt.client.ui.components.table.HasTooltip;
import fr.generali.accueilclient.gwt.client.ui.components.tooltip.TooltipPopupPanel;

/**
 * Cellule à l'écoute des évènement de la souris.<br>
 * Permet d'afficher un tooltip au survol.<br>
 * 
 * @param <T>
 * @param <R>
 */
public class MouseEventAwareEditableCell<T, U, R extends AbstractSafeHtmlRenderer<T>> extends
                AbstractEditableCell<T, U> implements HasTooltip<T> {

    private static final int TOOLTIP_HIDING_DELAY = 80;

    private static final int PADDING_RIGHT = 5;

    private static final int PADDING_LEFT = 20;

    private static final String CLICK_EVENT = "click";

    private static final String MOUSE_OVER_EVENT = "mouseover";

    private static final String MOUSE_OUT_EVENT = "mouseout";

    private R renderer;

    protected TooltipPopupPanel tooltip;

    private String tooltipValue;

    private boolean isTooltipDisplayed = false;

    private EventTarget lastEventTarget;

    Timer timer = new Timer() {
        @Override
        public void run() {
            hideTooltip();
        }
    };

    /**
     * Construct a new ButtonCell that will use a given {@link SafeHtmlRenderer}
     * .
     * 
     * @param renderer a {@link SafeHtmlRenderer SafeHtmlRenderer<String>}
     *            instance
     */
    public MouseEventAwareEditableCell(R renderer) {
        super(CLICK_EVENT, MOUSE_OVER_EVENT, MOUSE_OUT_EVENT);
        this.renderer = renderer;
        tooltip = new TooltipPopupPanel();
    }

    @Override
    public void onBrowserEvent(Context context, Element parent, T value, NativeEvent event, ValueUpdater<T> valueUpdater) {
        super.onBrowserEvent(context, parent, value, event, valueUpdater);
        if (CLICK_EVENT.equals(event.getType())) {
            EventTarget eventTarget = event.getEventTarget();
            if (!Element.is(eventTarget)) {
                return;
            }
            if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
                // Ignore clicks that occur outside of the main element.
                onEnterKeyDown(context, parent, value, event, valueUpdater);
            }
        }
        if (MOUSE_OVER_EVENT.equals(event.getType())) {
            EventTarget eventTarget = event.getEventTarget();
            if (!Element.is(eventTarget)) {
                return;
            }

            if (shouldDisplayTooltip() && !Objects.equal(eventTarget, lastEventTarget)) {
                showTooltip(event.getClientX(), event.getClientY());
                lastEventTarget = eventTarget;
            }
        }
        if (MOUSE_OUT_EVENT.equals(event.getType())) {
            if (parent != tooltip.getElement()) {
                timer.schedule(TOOLTIP_HIDING_DELAY);
                lastEventTarget = null;
            }
        }
    }

    protected boolean shouldDisplayTooltip() {
        return false;
    }

    private void hideTooltip() {
        timer.cancel();
        if (isTooltipDisplayed) {
            isTooltipDisplayed = false;
            tooltip.hide();
        }
    }

    @Override
    public void showTooltip(int x, int y) {
        timer.cancel();
        tooltip.setText(tooltipValue);
        tooltip.setPopupPosition(x + PADDING_LEFT, y + PADDING_RIGHT);
        if (!isTooltipDisplayed) {
            tooltip.show();
            tooltip.setVisible(true);
        }
        isTooltipDisplayed = true;
    }

    @Override
    protected void onEnterKeyDown(Context context, Element parent, T value, NativeEvent event,
                    ValueUpdater<T> valueUpdater) {
        if (valueUpdater != null) {
            valueUpdater.update(value);
        }
    }

    @Override
    public void render(Context context, T value, SafeHtmlBuilder sb) {
        if (value != null) {
            sb.append(renderer.render(value));
        }
    }

    @Override
    public void setTooltipValue(String tooltipValue) {
        this.tooltipValue = tooltipValue;
    }

    @Override
    public boolean isEditing(Context context, Element parent, T value) {
        return false;
    }
}
